import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { store } from '../utils/store';
import socketService from '../utils/socket';

export default class CityScene {
  constructor(container) {
    // Scene elements
    this.container = container;
    this.scene = null;
    this.camera = null;
    this.renderer = null;
    this.controls = null;
    this.raycaster = new THREE.Raycaster();
    this.mouse = new THREE.Vector2();
    
    // Objects and models
    this.city = null;
    this.cars = [];
    this.dealers = [];
    this.avatars = {};
    
    // Player character
    this.player = null;
    this.moveSpeed = 0.2;
    this.moveDirection = new THREE.Vector3();
    this.playerPosition = new THREE.Vector3(0, 0, 0);
    this.moveForward = false;
    this.moveBackward = false;
    this.moveLeft = false;
    this.moveRight = false;
    this.canJump = false;
    this.isFirstPerson = false;
    this.thirdPersonDistance = 5;
    
    // Mouse controls
    this.mouseDown = false;
    this.mouseSensitivity = 0.002;
    this.playerRotation = new THREE.Euler(0, 0, 0, 'YXZ');
    this.cameraRotation = new THREE.Euler(0, 0, 0, 'YXZ');
    this.targetRotation = new THREE.Euler(0, 0, 0, 'YXZ');
    this.rotationVelocity = new THREE.Vector2(0, 0);
    this.mouseMovement = new THREE.Vector2(0, 0);
    this.previousMousePosition = new THREE.Vector2(0, 0);
    this.pointerLocked = false;
    
    // Initialize scene
    this.init();
    
    // Start animation loop
    this.animate();
    
    // Add event listeners
    this.setupEventListeners();
  }
  
  // Initialize the scene
  init() {
    console.log('Initializing 3D scene');
    console.log('Container dimensions:', this.container.clientWidth, this.container.clientHeight);
    
    // Create scene
    this.scene = new THREE.Scene();
    this.scene.background = new THREE.Color(0xadd8e6); // Light blue sky
    
    // Create camera
    this.camera = new THREE.PerspectiveCamera(
      75, 
      this.container.clientWidth / this.container.clientHeight || 1, 
      0.1, 
      1000
    );
    this.camera.position.set(0, 10, 20);
    
    // Add fog for atmospheric effect
    this.scene.fog = new THREE.FogExp2(0xadd8e6, 0.01);
    
    // Create renderer
    this.renderer = new THREE.WebGLRenderer({ antialias: true });
    this.renderer.setSize(this.container.clientWidth || window.innerWidth, this.container.clientHeight || window.innerHeight);
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    this.container.appendChild(this.renderer.domElement);
    
    // Add orbit controls
    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    this.controls.maxPolarAngle = Math.PI / 2; // Limit vertical rotation
    this.controls.minDistance = 5;
    this.controls.maxDistance = 100;
    this.controls.enableDamping = true;
    this.controls.dampingFactor = 0.05;
    
    // Add lights
    this.addLights();
    
    // Add ground
    this.addGround();
    
    // Build simple city
    this.buildCity();
    
    // Add player character
    this.addPlayer();
    
    console.log('3D scene initialized');
  }
  
  // Add player character
  addPlayer() {
    // Create player character
    const playerGeometry = new THREE.CylinderGeometry(0.5, 0.5, 2, 8);
    const playerMaterial = new THREE.MeshStandardMaterial({
      color: 0xff0000, // Red player
      roughness: 0.5,
      metalness: 0.3
    });
    
    this.player = new THREE.Mesh(playerGeometry, playerMaterial);
    this.player.position.set(0, 1, 0); // Start at center, above ground
    this.player.castShadow = true;
    this.player.receiveShadow = true;
    this.player.userData = { type: 'player' };
    
    // Add player head
    const headGeometry = new THREE.SphereGeometry(0.4, 16, 16);
    const headMaterial = new THREE.MeshStandardMaterial({
      color: 0xffcccc,
      roughness: 0.5,
      metalness: 0.1
    });
    const head = new THREE.Mesh(headGeometry, headMaterial);
    head.position.y = 0.9; // Position on top of cylinder
    this.player.add(head);
    
    // Add direction indicator (to show front direction)
    const indicatorGeometry = new THREE.ConeGeometry(0.2, 0.5, 8);
    const indicatorMaterial = new THREE.MeshStandardMaterial({
      color: 0x00ff00,
      roughness: 0.5,
      metalness: 0.3
    });
    const indicator = new THREE.Mesh(indicatorGeometry, indicatorMaterial);
    indicator.position.set(0, 0.3, 0.6); // Position in front
    indicator.rotation.x = Math.PI / 2; // Rotate to point forward
    this.player.add(indicator);
    
    // Create player label
    const nameSprite = this.createTextSprite("Player");
    nameSprite.position.y = 1.5;
    this.player.add(nameSprite);
    
    this.scene.add(this.player);
    
    // Store initial position
    this.playerPosition.copy(this.player.position);
    
    // Position camera behind player
    this.updateCameraPosition();
    
    // Disable orbit controls to use player-centric controls
    this.controls.enabled = false;
  }
  
  // Update camera position based on player
  updateCameraPosition() {
    if (this.isFirstPerson) {
      // First person view (camera at player's head)
      this.camera.position.copy(this.player.position);
      this.camera.position.y += 1; // Eye level
    } else {
      // Third person view (camera follows behind player)
      const cameraOffset = new THREE.Vector3(0, 3, this.thirdPersonDistance);
      
      // Rotate the offset vector based on player rotation
      cameraOffset.applyEuler(new THREE.Euler(0, this.player.rotation.y, 0));
      
      // Calculate the camera position
      this.camera.position.copy(this.player.position).add(cameraOffset);
      
      // Make the camera look at the player's head
      this.camera.lookAt(
        this.player.position.x, 
        this.player.position.y + 1, 
        this.player.position.z
      );
    }
  }
  
  // Handle mouse movement for camera rotation
  handleMouseMove(event) {
    if (!this.mouseDown && !this.pointerLocked) return;
    
    // Calculate mouse movement
    const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
    const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
    
    // Update rotation based on mouse movement
    this.playerRotation.y -= movementX * this.mouseSensitivity;
    
    // Limit vertical rotation to avoid flipping
    this.cameraRotation.x -= movementY * this.mouseSensitivity;
    this.cameraRotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.cameraRotation.x));
    
    // Apply rotation to player
    this.player.rotation.y = this.playerRotation.y;
    
    // In first person mode, also rotate camera vertically
    if (this.isFirstPerson) {
      this.camera.rotation.x = this.cameraRotation.x;
    }
    
    // Update camera position
    this.updateCameraPosition();
  }
  
  // Handle player movement
  movePlayer() {
    // Reset movement direction
    this.moveDirection.set(0, 0, 0);
    
    // Calculate movement direction
    if (this.moveForward) this.moveDirection.z -= 1;
    if (this.moveBackward) this.moveDirection.z += 1;
    if (this.moveLeft) this.moveDirection.x -= 1;
    if (this.moveRight) this.moveDirection.x += 1;
    
    // Normalize movement direction if moving diagonally
    if (this.moveDirection.lengthSq() > 0) {
      this.moveDirection.normalize();
      
      // Apply movement speed
      this.moveDirection.multiplyScalar(this.moveSpeed);
      
      // Rotate movement vector based on player rotation
      this.moveDirection.applyEuler(new THREE.Euler(0, this.player.rotation.y, 0));
      
      // Update player position
      this.player.position.x += this.moveDirection.x;
      this.player.position.z += this.moveDirection.z;
      
      // Update camera position
      this.updateCameraPosition();
      
      // Update store with new position (for networking)
      store.updatePosition(this.player.position.x, this.player.position.y, this.player.position.z);
      
      // Send position update to server
      socketService.sendPosition(this.player.position.x, this.player.position.y, this.player.position.z);
    }
  }
  
  // Add lights to the scene
  addLights() {
    // Ambient light for general illumination
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    this.scene.add(ambientLight);
    
    // Directional light for shadows
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
    directionalLight.position.set(50, 200, 100);
    directionalLight.castShadow = true;
    directionalLight.shadow.mapSize.width = 1024;
    directionalLight.shadow.mapSize.height = 1024;
    
    // Set up shadow camera
    const d = 100;
    directionalLight.shadow.camera.left = -d;
    directionalLight.shadow.camera.right = d;
    directionalLight.shadow.camera.top = d;
    directionalLight.shadow.camera.bottom = -d;
    directionalLight.shadow.camera.near = 0.5;
    directionalLight.shadow.camera.far = 500;
    
    this.scene.add(directionalLight);
    
    // Add hemisphere light (sky/ground)
    const hemisphereLight = new THREE.HemisphereLight(0xadd8e6, 0x095f09, 0.5);
    this.scene.add(hemisphereLight);
  }
  
  // Add ground
  addGround() {
    const groundGeometry = new THREE.PlaneGeometry(1000, 1000);
    const groundMaterial = new THREE.MeshStandardMaterial({ 
      color: 0x1a5e1a, // Dark green
      roughness: 0.8,
      metalness: 0.2
    });
    const ground = new THREE.Mesh(groundGeometry, groundMaterial);
    ground.rotation.x = -Math.PI / 2; // Rotate to be horizontal
    ground.receiveShadow = true;
    ground.userData = { type: 'ground' };
    this.scene.add(ground);
  }
  
  // Build simple city with buildings
  buildCity() {
    console.log('Building city...');
    // City center
    this.createBuilding(0, 0, 10, 10, 5, 0x888888); // Central plaza
    
    // Create city blocks in a grid
    for (let x = -50; x <= 50; x += 20) {
      for (let z = -50; z <= 50; z += 20) {
        // Skip center
        if (x === 0 && z === 0) continue;
        
        // Random building
        const height = 3 + Math.random() * 10;
        const width = 5 + Math.random() * 8;
        const depth = 5 + Math.random() * 8;
        
        // Random color
        const colors = [0x888888, 0x996633, 0x3333cc, 0x339933];
        const color = colors[Math.floor(Math.random() * colors.length)];
        
        this.createBuilding(x, z, width, depth, height, color);
      }
    }
    
    // Create roads
    this.createRoads();
    
    // Add interactive objects
    this.addInteractiveObjects();
    
    console.log('City built successfully');
  }
  
  // Create a building
  createBuilding(x, z, width, depth, height, color) {
    const geometry = new THREE.BoxGeometry(width, height, depth);
    const material = new THREE.MeshStandardMaterial({ 
      color,
      roughness: 0.7,
      metalness: 0.2
    });
    const building = new THREE.Mesh(geometry, material);
    building.position.set(x, height / 2, z);
    building.castShadow = true;
    building.receiveShadow = true;
    building.userData = { type: 'building' };
    this.scene.add(building);
    
    return building;
  }
  
  // Create roads
  createRoads() {
    // Horizontal roads
    for (let z = -50; z <= 50; z += 20) {
      const roadGeometry = new THREE.PlaneGeometry(120, 5);
      const roadMaterial = new THREE.MeshStandardMaterial({ 
        color: 0x222222, // Dark gray
        roughness: 0.9
      });
      const road = new THREE.Mesh(roadGeometry, roadMaterial);
      road.rotation.x = -Math.PI / 2;
      road.position.set(0, 0.01, z); // Slightly above ground
      road.receiveShadow = true;
      this.scene.add(road);
    }
    
    // Vertical roads
    for (let x = -50; x <= 50; x += 20) {
      const roadGeometry = new THREE.PlaneGeometry(5, 120);
      const roadMaterial = new THREE.MeshStandardMaterial({ 
        color: 0x222222, // Dark gray
        roughness: 0.9
      });
      const road = new THREE.Mesh(roadGeometry, roadMaterial);
      road.rotation.x = -Math.PI / 2;
      road.position.set(x, 0.01, 0); // Slightly above ground
      road.receiveShadow = true;
      this.scene.add(road);
    }
  }
  
  // Add a car to the scene
  addCar(carData) {
    // Create simple car representation
    const carGeometry = new THREE.BoxGeometry(3, 1.5, 4);
    const carMaterial = new THREE.MeshStandardMaterial({ 
      color: this.getCarColor(carData.color) || 0xff0000, // Default to red if no color is specified
      roughness: 0.5,
      metalness: 0.7
    });
    
    const car = new THREE.Mesh(carGeometry, carMaterial);
    car.position.set(carData.position.x, 0.75, carData.position.z);
    car.castShadow = true;
    car.receiveShadow = true;
    car.userData = { 
      type: 'car',
      carId: carData.car_id,
      carData: carData 
    };
    
    // Add car name label
    const nameSprite = this.createTextSprite(carData.brand + ' ' + carData.model);
    nameSprite.position.y = 2;
    car.add(nameSprite);
    
    this.scene.add(car);
    this.cars.push(car);
    
    return car;
  }
  
  // Convert car color string to hex color
  getCarColor(colorName) {
    const colorMap = {
      'Black': 0x000000,
      'White': 0xffffff,
      'Red': 0xff0000,
      'Blue': 0x0000ff,
      'Silver': 0xc0c0c0,
      'Gray': 0x808080,
      'Green': 0x008000,
      'Yellow': 0xffff00
    };
    
    return colorMap[colorName] || 0xff0000;
  }
  
  // Add dealer location to the scene
  addDealer(dealerData) {
    // Create dealer building
    const dealerGeometry = new THREE.BoxGeometry(8, 6, 8);
    const dealerMaterial = new THREE.MeshStandardMaterial({ 
      color: 0x0088ff, // Blue building
      roughness: 0.5,
      metalness: 0.3
    });
    
    const dealer = new THREE.Mesh(dealerGeometry, dealerMaterial);
    dealer.position.set(dealerData.position.x, 3, dealerData.position.z);
    dealer.castShadow = true;
    dealer.receiveShadow = true;
    dealer.userData = { 
      type: 'dealer',
      dealerId: dealerData.dealer_id,
      dealerData: dealerData 
    };
    
    // Add dealer name floating above
    const nameSprite = this.createTextSprite(dealerData.dealer_name);
    nameSprite.position.y = 6;
    dealer.add(nameSprite);
    
    this.scene.add(dealer);
    this.dealers.push(dealer);
    
    return dealer;
  }
  
  // Add avatar for another user
  addAvatar(userData) {
    if (this.avatars[userData.id]) {
      // If avatar already exists, just update position
      this.updateAvatarPosition(userData.id, userData.position);
      return;
    }
    
    // Create simple avatar representation
    const geometry = new THREE.CylinderGeometry(0.5, 0.5, 2, 8);
    const material = new THREE.MeshStandardMaterial({ 
      color: 0xffff00, // Yellow avatar
      roughness: 0.5,
      metalness: 0.3
    });
    
    const avatar = new THREE.Mesh(geometry, material);
    avatar.position.set(
      userData.position.x || 0,
      1, // Height above ground
      userData.position.z || 0
    );
    avatar.castShadow = true;
    avatar.userData = { 
      type: 'avatar',
      userId: userData.id 
    };
    
    // Add username floating above
    const nameSprite = this.createTextSprite(userData.username);
    nameSprite.position.y = 2;
    avatar.add(nameSprite);
    
    this.scene.add(avatar);
    this.avatars[userData.id] = avatar;
  }
  
  // Create text sprite for floating labels
  createTextSprite(text) {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    canvas.width = 256;
    canvas.height = 64;
    
    context.font = '24px Arial';
    context.textAlign = 'center';
    context.fillStyle = 'white';
    context.strokeStyle = 'black';
    context.lineWidth = 4;
    context.strokeText(text, 128, 32);
    context.fillText(text, 128, 32);
    
    const texture = new THREE.CanvasTexture(canvas);
    const material = new THREE.SpriteMaterial({ map: texture });
    const sprite = new THREE.Sprite(material);
    sprite.scale.set(5, 1.25, 1);
    
    return sprite;
  }
  
  // Update avatar position
  updateAvatarPosition(userId, position) {
    if (this.avatars[userId]) {
      const avatar = this.avatars[userId];
      avatar.position.x = position.x;
      avatar.position.z = position.z;
    }
  }
  
  // Update user's own position
  updateUserPosition(x, z) {
    // Update position in store and send to server
    socketService.sendPosition(x, 1, z);
  }
  
  // Set up event listeners
  setupEventListeners() {
    // Handle window resize
    window.addEventListener('resize', this.onWindowResize.bind(this));
    
    // Handle mouse click for object interaction
    this.container.addEventListener('click', this.onMouseClick.bind(this));
    
    // Handle keyboard input for movement
    document.addEventListener('keydown', this.onKeyDown.bind(this));
    document.addEventListener('keyup', this.onKeyUp.bind(this));
    
    // Handle mouse events for camera control
    this.container.addEventListener('mousedown', this.onMouseDown.bind(this));
    document.addEventListener('mouseup', this.onMouseUp.bind(this));
    document.addEventListener('mousemove', this.handleMouseMove.bind(this));
    
    // Handle pointer lock for better mouse control
    this.container.addEventListener('dblclick', this.requestPointerLock.bind(this));
    document.addEventListener('pointerlockchange', this.onPointerLockChange.bind(this));
    document.addEventListener('mozpointerlockchange', this.onPointerLockChange.bind(this));
    document.addEventListener('webkitpointerlockchange', this.onPointerLockChange.bind(this));
    
    // Toggle view mode (first person / third person)
    document.addEventListener('keydown', (event) => {
      if (event.key === 'v') {
        this.isFirstPerson = !this.isFirstPerson;
        this.updateCameraPosition();
      }
    });
  }
  
  // Handle window resize
  onWindowResize() {
    if (!this.camera || !this.renderer || !this.container) return;
    
    console.log('Resizing 3D scene');
    this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
  }
  
  // Handle mouse click
  onMouseClick(event) {
    // Calculate mouse position in normalized device coordinates
    this.mouse.x = (event.clientX / this.container.clientWidth) * 2 - 1;
    this.mouse.y = -(event.clientY / this.container.clientHeight) * 2 + 1;
    
    // Update the picking ray with the camera and mouse position
    this.raycaster.setFromCamera(this.mouse, this.camera);
    
    // Calculate objects intersecting the picking ray
    const intersects = this.raycaster.intersectObjects(this.scene.children, true);
    
    if (intersects.length > 0) {
      // Get the first clicked object
      let object = intersects[0].object;
      
      // If we clicked a child object, find the parent
      while (object.parent && object.parent !== this.scene) {
        object = object.parent;
      }
      
      // Handle different object types
      if (object.userData.type === 'car') {
        console.log('Car clicked:', object.userData.carData);
        store.selectCar(object.userData.carData);
      } else if (object.userData.type === 'dealer') {
        console.log('Dealer clicked:', object.userData.dealerData);
        store.selectDealer(object.userData.dealerData);
      } else if (object.userData.type === 'ground') {
        // Move to clicked position on ground
        const point = intersects[0].point;
        this.updateUserPosition(point.x, point.z);
      }
    }
  }
  
  // Handle mouse down
  onMouseDown(event) {
    if (event.button === 2) { // Right mouse button
      this.mouseDown = true;
      this.previousMousePosition.x = event.clientX;
      this.previousMousePosition.y = event.clientY;
      
      // Prevent context menu
      this.container.addEventListener('contextmenu', this.onContextMenu.bind(this));
    }
  }
  
  // Handle mouse up
  onMouseUp(event) {
    if (event.button === 2) { // Right mouse button
      this.mouseDown = false;
    }
  }
  
  // Prevent context menu on right click
  onContextMenu(event) {
    event.preventDefault();
    return false;
  }
  
  // Request pointer lock for better mouse control
  requestPointerLock() {
    if (this.container.requestPointerLock) {
      this.container.requestPointerLock();
    } else if (this.container.mozRequestPointerLock) {
      this.container.mozRequestPointerLock();
    } else if (this.container.webkitRequestPointerLock) {
      this.container.webkitRequestPointerLock();
    }
  }
  
  // Handle pointer lock change
  onPointerLockChange() {
    if (document.pointerLockElement === this.container ||
        document.mozPointerLockElement === this.container ||
        document.webkitPointerLockElement === this.container) {
      // Pointer locked
      this.pointerLocked = true;
      console.log('Pointer locked - mouse look enabled');
    } else {
      // Pointer unlocked
      this.pointerLocked = false;
      console.log('Pointer unlocked - mouse look disabled');
    }
  }
  
  // Handle key down events
  onKeyDown(event) {
    switch (event.key) {
      case 'w':
      case 'ArrowUp':
        this.moveForward = true;
        break;
      case 's':
      case 'ArrowDown':
        this.moveBackward = true;
        break;
      case 'a':
      case 'ArrowLeft':
        this.moveLeft = true;
        break;
      case 'd':
      case 'ArrowRight':
        this.moveRight = true;
        break;
      case 'f':
      case 'F':
        this.tryInteract();
        break;
      case ' ': // Space bar
        if (this.canJump) {
          // Add jump functionality if needed
        }
        break;
      case 'Escape':
        // Exit pointer lock
        if (document.exitPointerLock) {
          document.exitPointerLock();
        } else if (document.mozExitPointerLock) {
          document.mozExitPointerLock();
        } else if (document.webkitExitPointerLock) {
          document.webkitExitPointerLock();
        }
        break;
    }
  }
  
  // Handle key up events
  onKeyUp(event) {
    switch (event.key) {
      case 'w':
      case 'ArrowUp':
        this.moveForward = false;
        break;
      case 's':
      case 'ArrowDown':
        this.moveBackward = false;
        break;
      case 'a':
      case 'ArrowLeft':
        this.moveLeft = false;
        break;
      case 'd':
      case 'ArrowRight':
        this.moveRight = false;
        break;
    }
  }
  
  // Populate cars in the scene
  populateCars(carsData) {
    // Clear existing cars
    for (const car of this.cars) {
      this.scene.remove(car);
    }
    this.cars = [];
    
    // Add cars with positions distributed around the city
    carsData.forEach((carData, index) => {
      // Position cars along the roads
      const x = -40 + (index % 5) * 20;
      const z = -40 + Math.floor(index / 5) * 20;
      
      carData.position = { x, z };
      this.addCar(carData);
    });
  }
  
  // Populate dealers in the scene
  populateDealers(dealersData) {
    // Clear existing dealers
    for (const dealer of this.dealers) {
      this.scene.remove(dealer);
    }
    this.dealers = [];
    
    // Add dealers at specific locations
    dealersData.forEach((dealerData, index) => {
      // Position dealers at corners
      const positions = [
        { x: -40, z: -40 },
        { x: 40, z: -40 },
        { x: 40, z: 40 },
        { x: -40, z: 40 }
      ];
      
      const posIndex = index % positions.length;
      dealerData.position = positions[posIndex];
      
      this.addDealer(dealerData);
    });
  }
  
  // Animation loop
  animate() {
    requestAnimationFrame(this.animate.bind(this));
    
    // Update player movement
    if (this.player) {
      this.movePlayer();
    }
    
    // Update controls
    if (this.controls && this.controls.enabled) {
      this.controls.update();
    }
    
    // Render scene
    if (this.renderer && this.scene && this.camera) {
      this.renderer.render(this.scene, this.camera);
    }
  }
  
  // Add interactive objects to the scene
  addInteractiveObjects() {
    console.log('Adding interactive objects');
    
    // List of positions for interactive objects
    const positions = [
      { x: 10, z: 10 },
      { x: -15, z: 15 },
      { x: 20, z: -10 },
      { x: -25, z: -25 }
    ];
    
    // Create interactive objects
    positions.forEach((pos, index) => {
      this.createInteractiveObject(pos.x, pos.z, index);
    });
  }
  
  // Create an interactive object
  createInteractiveObject(x, z, index) {
    // Create a glowing object
    const geometry = new THREE.SphereGeometry(1, 16, 16);
    const material = new THREE.MeshStandardMaterial({ 
      color: 0x00ffff, // Cyan
      emissive: 0x00ffff,
      emissiveIntensity: 0.5,
      roughness: 0.2,
      metalness: 0.8
    });
    
    const interactiveObject = new THREE.Mesh(geometry, material);
    interactiveObject.position.set(x, 1.5, z);
    interactiveObject.castShadow = true;
    interactiveObject.receiveShadow = true;
    
    // Add custom userData for interaction
    interactiveObject.userData = { 
      type: 'interactive',
      id: `interactive-${index}`,
      name: `Interactive Object ${index + 1}`,
      canInteract: true,
      messageIndex: 0,
      messages: [
        "Hello there! Press F to interact with me.",
        "Nice to meet you! How's your day?",
        "I'm just a 3D object living in a digital world!",
        "Feel free to explore the city more!"
      ]
    };
    
    // Add floating indicator
    const labelSprite = this.createTextSprite(`💬 Interactive ${index + 1} (F)`);
    labelSprite.position.y = 2;
    interactiveObject.add(labelSprite);
    
    // Add visual indicator for interaction
    const indicatorGeometry = new THREE.RingGeometry(1.5, 1.7, 32);
    const indicatorMaterial = new THREE.MeshBasicMaterial({ 
      color: 0x00ffff,
      side: THREE.DoubleSide,
      transparent: true,
      opacity: 0.5
    });
    
    const indicator = new THREE.Mesh(indicatorGeometry, indicatorMaterial);
    indicator.rotation.x = -Math.PI / 2; // Make it horizontal
    indicator.position.y = -1;
    interactiveObject.add(indicator);
    
    // Add animation
    this.animateInteractiveObject(interactiveObject, indicator);
    
    this.scene.add(interactiveObject);
    return interactiveObject;
  }
  
  // Animate interactive object
  animateInteractiveObject(object, indicator) {
    // Initial time offset based on object position
    const timeOffset = object.position.x + object.position.z;
    
    // Animation update function
    const animate = () => {
      // Hover animation
      object.position.y = 1.5 + Math.sin((Date.now() + timeOffset) * 0.001) * 0.2;
      
      // Rotation animation
      object.rotation.y += 0.01;
      
      // Indicator pulse
      indicator.scale.set(
        1 + Math.sin((Date.now() + timeOffset) * 0.002) * 0.2,
        1 + Math.sin((Date.now() + timeOffset) * 0.002) * 0.2,
        1
      );
      
      requestAnimationFrame(animate);
    };
    
    // Start animation
    animate();
  }
  
  // Show 3D chat bubble
  showChatBubble(object, message) {
    // Remove old chat bubble if exists
    if (object.chatBubble) {
      object.remove(object.chatBubble);
    }
    
    // Create chat bubble container
    const chatBubble = new THREE.Group();
    chatBubble.position.set(0, 3, 0);
    
    // Create background panel
    const bubbleWidth = 6;
    const bubbleHeight = 3;
    const panelGeometry = new THREE.PlaneGeometry(bubbleWidth, bubbleHeight);
    const panelMaterial = new THREE.MeshBasicMaterial({
      color: 0xffffff,
      transparent: true,
      opacity: 0.8,
      side: THREE.DoubleSide
    });
    
    const panel = new THREE.Mesh(panelGeometry, panelMaterial);
    chatBubble.add(panel);
    
    // Create text sprite
    const textSprite = this.createTextSprite(message, 0x000000, 0xffffff);
    textSprite.scale.set(bubbleWidth * 0.8, bubbleHeight * 0.8, 1);
    chatBubble.add(textSprite);
    
    // Create chat bubble tail
    const tailGeometry = new THREE.BufferGeometry();
    const tailVertices = new Float32Array([
      -0.5, -bubbleHeight/2, 0,
      0.5, -bubbleHeight/2, 0,
      0, -bubbleHeight/2 - 1, 0
    ]);
    tailGeometry.setAttribute('position', new THREE.BufferAttribute(tailVertices, 3));
    
    const tailMaterial = new THREE.MeshBasicMaterial({
      color: 0xffffff,
      transparent: true,
      opacity: 0.8,
      side: THREE.DoubleSide
    });
    
    const tail = new THREE.Mesh(tailGeometry, tailMaterial);
    chatBubble.add(tail);
    
    // Always face camera
    const lookAtCamera = () => {
      if (chatBubble && this.camera) {
        chatBubble.lookAt(this.camera.position);
      }
      requestAnimationFrame(lookAtCamera);
    };
    lookAtCamera();
    
    // Add to object
    object.add(chatBubble);
    object.chatBubble = chatBubble;
    
    // Auto-hide after 5 seconds
    setTimeout(() => {
      if (object && object.chatBubble === chatBubble) {
        object.remove(chatBubble);
        object.chatBubble = null;
      }
    }, 5000);
  }
  
  // Try to interact with nearby objects
  tryInteract() {
    // Distance for interaction
    const interactionDistance = 5;
    
    // Find all interactive objects in the scene
    const interactiveObjects = [];
    this.scene.traverse((object) => {
      if (object.userData && object.userData.type === 'interactive') {
        interactiveObjects.push(object);
      }
    });
    
    // Calculate distance to each interactive object
    const playerPosition = this.player.position.clone();
    
    // Sort objects by distance
    interactiveObjects.sort((a, b) => {
      const distA = a.position.distanceTo(playerPosition);
      const distB = b.position.distanceTo(playerPosition);
      return distA - distB;
    });
    
    // Interact with the closest object within range
    const closestObject = interactiveObjects[0];
    if (closestObject && closestObject.position.distanceTo(playerPosition) <= interactionDistance) {
      this.interactWithObject(closestObject);
    } else {
      console.log('Nothing to interact with nearby');
      
      // Optionally show a UI message
      if (store.showSystemMessage) {
        store.showSystemMessage('Nothing to interact with nearby');
      }
    }
  }
  
  // Interact with object
  interactWithObject(object) {
    console.log('Interacting with', object.userData.name);
    
    // Get next message from rotation
    const messageIndex = object.userData.messageIndex % object.userData.messages.length;
    const message = object.userData.messages[messageIndex];
    
    // Update message index for next time
    object.userData.messageIndex++;
    
    // Show chat bubble with message
    this.showChatBubble(object, message);
    
    // Optionally add chat message to chat system
    const chatMessage = {
      senderId: object.userData.id,
      senderName: object.userData.name,
      message: message,
      timestamp: new Date().toISOString()
    };
    
    // Add to store if available
    if (store.addChatMessage) {
      store.addChatMessage(chatMessage);
    }
  }
  
  // Create text sprite for floating labels
  createTextSprite(text, textColor = 0xffffff, backgroundColor = null) {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    canvas.width = 512;
    canvas.height = 128;
    
    // Background if specified
    if (backgroundColor !== null) {
      context.fillStyle = `#${backgroundColor.toString(16).padStart(6, '0')}`;
      context.fillRect(0, 0, canvas.width, canvas.height);
    }
    
    // Text styling
    context.font = '32px Arial';
    context.textAlign = 'center';
    context.textBaseline = 'middle';
    
    // Text color
    context.fillStyle = `#${textColor.toString(16).padStart(6, '0')}`;
    
    // Add stroke for better visibility
    context.strokeStyle = backgroundColor === null ? 'black' : 'white';
    context.lineWidth = 4;
    
    // Handle multi-line text
    const lines = text.split('\n');
    const lineHeight = 36;
    const startY = canvas.height / 2 - (lineHeight * (lines.length - 1)) / 2;
    
    lines.forEach((line, index) => {
      const y = startY + index * lineHeight;
      context.strokeText(line, canvas.width / 2, y);
      context.fillText(line, canvas.width / 2, y);
    });
    
    // Create sprite
    const texture = new THREE.CanvasTexture(canvas);
    texture.needsUpdate = true;
    
    const material = new THREE.SpriteMaterial({ 
      map: texture,
      transparent: true
    });
    
    const sprite = new THREE.Sprite(material);
    sprite.scale.set(5, 1.25, 1);
    
    return sprite;
  }
} 